//////////////////////////////////////////////////////////////////////////
/// \file:  TextFile.cpp
/// Copyright (Tencent) 2007 - All Rights Reserved
/// Author: robetma.
/// Date:   [2008/06/26]
/// Description:
///     
//////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "textfile.h"


//	Base constructor
CTextFileBase::CTextFileBase()
		: m_hFile(INVALID_HANDLE_VALUE),
		m_nBuffPos(-1),
		m_unknownChar(0),
		m_bDatalLost(false),
		m_codepage(CP_ACP)
{}

//	Base destructor
CTextFileBase::~CTextFileBase()
{
	Close();
}

//	Set which character to use when conversion can't be done
void CTextFileBase::SetUnknownChar(const char unknown)
{
	m_unknownChar = unknown;
}

//	True if data was lost during conversion
bool CTextFileBase::IsDataLost() const
{
	return m_bDatalLost;
}

//	Reset the data lost flag
void CTextFileBase::ResetDataLostFlag()
{
	m_bDatalLost = false;
}

//	Make sure we have a legal value for code page to use when
//	converting string. Used for debugging only.
#define IsLegalCodePage(cp)\
	(cp == CP_ACP ||\
	cp == CP_MACCP ||\
	cp == CP_OEMCP ||\
	cp == CP_SYMBOL ||\
	cp == CP_THREAD_ACP ||\
	cp == CP_UTF7 ||\
	cp == CP_UTF8 ||\
	IsValidCodePage(cp))


//	(Windows-specific) Set codepage to use when working with non-unicode strings.
void CTextFileBase::SetCodePage(const UINT codepage)
{
	ASSERT(IsLegalCodePage(codepage));
	m_codepage = codepage;
}

//	(Windows-specific) Get codepage to use when working with non-unicode strings.
UINT CTextFileBase::GetCodePage() const
{
	return m_codepage;
}

//	(Windows-specific) Convert string to wstring with current codepage
inline void CTextFileBase::CharToWstring(const char* from, std::wstring &to) const
{
	ConvertCharToWstring(from, to, m_codepage);
}

//	(Windows-specific) Convert wstring to string with current codepage
inline void CTextFileBase::WcharToString(const wchar_t* from, std::string &to)
{
	ConvertWcharToString(from, to, m_codepage, &m_bDatalLost, m_unknownChar);
}


//	(Windows-specific) Convert char* to wstring
void CTextFileBase::ConvertCharToWstring(const char* from, std::wstring &to, UINT codepage)
{
	to.clear();

	ASSERT(IsLegalCodePage(codepage));
	//	Use api convert routine
	int nLen = ::MultiByteToWideChar(codepage,
	                                 0,
	                                 from,
	                                 -1,
	                                 NULL,
	                                 0);

	//	if nLen == 0, some unknown codepage was probably used.
	ASSERT(nLen);
	if ( 0 == nLen )
	{
		return;
	}

	wchar_t* pBuffer = new wchar_t[nLen+2];

	::MultiByteToWideChar(codepage,
	                      0,
	                      from,
	                      -1,
	                      pBuffer,
	                      nLen);

	to = pBuffer;
	delete [] pBuffer;
}

//	(Windows-specific) Convert wchar_* to string
void CTextFileBase::ConvertWcharToString(const wchar_t* from,
										 std::string &to,
										 UINT codepage,
										 bool* bDataLost,
										 char unknownchar)
{
	to.clear();

	ASSERT( IsLegalCodePage(codepage) );

	int nLen = 	::WideCharToMultiByte(codepage,
	                                  0,
	                                  from,
	                                  -1,
	                                  NULL,
	                                  0,
	                                  NULL,
	                                  NULL);

	//	if nLen == 0, some unknown codepage was probably used.
	ASSERT(nLen);
	if ( 0 == nLen )
	{
		return;
	}

	char* pBuffer = new char[nLen+2];
	BOOL bUsedDefault = FALSE;

	::WideCharToMultiByte(codepage,
	                      0,
	                      from,
	                      -1,
	                      pBuffer,
	                      nLen,
	                      (unknownchar != 0 ? &unknownchar : NULL),
	                      (bDataLost != NULL ? &bUsedDefault : NULL));


	if ( NULL != bDataLost && FALSE != bUsedDefault )
	{
		*bDataLost = true;
	}

	to = pBuffer;
	delete [] pBuffer;
}

//	Return file encoding
CTextFileBase::TEXTENCODING CTextFileBase::GetEncoding() const
{
	return m_encoding;
};

//	Is file open?
int CTextFileBase::IsOpen()
{
	return INVALID_HANDLE_VALUE != m_hFile;
}

//	Close file
void CTextFileBase::Close()
{
	if ( IsOpen() )
	{
		::CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
	}
}


//	Create textfile
CTextFileWrite::CTextFileWrite(const FILENAMECHAR* filename,
							   TEXTENCODING encoding,
							   DWORD dwMethod/* = CREATE_ALWAYS*/)
{
	m_hFile = ::CreateFile(filename,
	                       GENERIC_WRITE,
	                       0,
	                       NULL,
	                       dwMethod,
	                       FILE_ATTRIBUTE_NORMAL,
	                       NULL);

	m_nBuffPos = -1;
	m_nBuffSize = 0;

	m_encoding = encoding;

	WriteBOM();
}

CTextFileWrite::~CTextFileWrite()
{
	Close();
}

void CTextFileWrite::WriteBOM()
{
	//	Write BOM
	if ( IsOpen() )
	{
		if ( UNI16_BE == m_encoding || UNI16_LE == m_encoding )
		{
			//	Write BOM
			WriteWchar( 0xFEFF );
		}
		else if ( UTF_8 == m_encoding )
		{
			//	Write UTF-8 BOM.  0xEF 0xBB 0xBF
			WriteByte(0xEF);
			WriteByte(0xBB);
			WriteByte(0xBF);
		}
	}
}

//	Write one byte
void CTextFileWrite::WriteByte(const unsigned char byte)
{
	//Instead of writing, save data in buffer and write when buffer is full
	if ( m_nBuffPos + 1 >= BUFFSIZE )
	{
		Flush();
	}

	++m_nBuffPos;
	m_buf[m_nBuffPos] = byte;
}

//	Write and empty buffer
void CTextFileWrite::Flush()
{
	DWORD nWritten = ULONG_MAX;
	if ( !::WriteFile(m_hFile, m_buf, m_nBuffPos + 1, &nWritten, NULL) )
	{
		//	Something bad has happened! Close file
		CTextFileBase::Close();

		//	Throw exception
		throw CTextFileException(GetLastError());
	}

	m_nBuffPos = -1;
}

void CTextFileWrite::WriteWchar(const wchar_t ch)
{
	//	Write HO byte first?
	if ( UNI16_BE == m_encoding )
	{
		//	Write HO byte
		WriteByte((unsigned char) (ch >> 8) );
		//	Write LO byte
		WriteByte((unsigned char) ch);
	}
	else if ( UNI16_LE == m_encoding )
	{
		//	Write LO byte
		WriteByte((unsigned char) ch);
		//	Write HO byte
		WriteByte((unsigned char) (ch >> 8) );
	}
	else
	{
		//	http://www.cl.cam.ac.uk/~mgk25/unicode.html#examples
		//	http://www.ietf.org/rfc/rfc3629.txt

		//	Just a single byte?
		if ( ch <= 0x7F )
		{
			//U-00000000 - U-0000007F:  0xxxxxxx
			WriteByte( (unsigned char) ch );
		}
		//	Two bytes?
		else if ( ch <= 0x7FF )
		{
			//U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
			WriteByte( (unsigned char) (0xC0 | (ch >> 6)) );
			WriteByte( (unsigned char) (0x80 | (ch&0x3F)) );
		}
		//	Three bytes?
		else if ( ch <= 0xFFFF )
		{
			//	U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
			WriteByte( (unsigned char) (0xE0 | (  ch >> 12)		));
			WriteByte( (unsigned char) (0x80 | ( (ch >> 6)&0x3F ) ));
			WriteByte( (unsigned char) (0x80 | ( ch&0x3F )		));
		}
		/* //UPS! I did some coding for UTF-32, may be useful in the future :-)
		//	Four bytes?
		else if(ch <= 0x1FFFFF)
		{
		//	U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  
		WriteByte( (unsigned char) (0xF0 | (  ch>>18)		 ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>12)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>6)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( ch&0xA0 )		));
		}
		//	Five bytes bytes?
		else if(ch <= 0x3FFFFFF)
		{
		//	U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
		WriteByte( (unsigned char) (0xF8 | (  ch>>24)	 	 ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>18)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>12)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>6)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( ch&0xA0 )		));
		}
		//	Five bytes bytes?
		else if(ch <= 0x7FFFFFFF)
		{
		//	U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
		WriteByte( (unsigned char) (0xFC | (  ch>>30)		 ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>24)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>18)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>12)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( (ch>>6)&0xA0 ) ));
		WriteByte( (unsigned char) (0xA0 | ( ch&0xA0 )		));
		}*/
	}
}

//	Write new line
void CTextFileWrite::WriteEndl()
{
	if ( ASCII == m_encoding )
	{
		WriteByte(0x0D);
		WriteByte(0x0A);
	}
	else
	{
		WriteWchar(0x0D);
		WriteWchar(0x0A);
	}
}


//	Write a c-string in ASCII.
//	In versions before 1.02 this function wrote directly to file,
//	no buffer was used. But sense WriteEndl() used buffer the file was
//	written incorrectly. Now buffer is used here too, this is the
//	fastest solution.
void CTextFileWrite::WriteAsciiString(const char* s)
{
	while (*s != '\0')
	{
		WriteByte(*s);
		++s;
	}
}


CTextFileWrite& CTextFileWrite::operator<< (const char* text)
{
	Write(text);

	return *this;
};

CTextFileWrite& CTextFileWrite::operator << (const std::string& text)
{
	Write(text.c_str());

	return *this;
}

CTextFileWrite& CTextFileWrite::operator << (const wchar_t* text)
{
	Write(text);

	return *this;
};

CTextFileWrite& CTextFileWrite::operator << (const std::wstring& text)
{
	Write(text.c_str());

	return *this;
}


CTextFileWrite& CTextFileWrite::operator << (const wchar_t wc)
{
	//	Not the perfect code, but it's easy!
	std::wstring text;
	text = wc;
	Write(text.c_str());

	return *this;
};

CTextFileWrite& CTextFileWrite::operator<< (const char c)
{
	//	Not the perfect code, but it's easy!
	std::string text;
	text = c;
	Write(text.c_str());

	return *this;
};

void CTextFileWrite::Write(const std::string& text)
{
	Write(text.c_str());
}

void CTextFileWrite::Write(const std::wstring& text)
{
	Write(text.c_str());
}

//	Write char*
void CTextFileWrite::Write(const char* text)
{
	//	ASCIItext file format?
	if ( ASCII == m_encoding )
	{
		WriteAsciiString(text);
	}
	else
	{
		//	Convert text to unicode
		std::wstring utext;
		CharToWstring(text, utext);

		//	OK, lets write unicode
		std::wstring::const_iterator i = utext.begin();
		for ( ; i < utext.end(); ++i )
		{
			WriteWchar(*i);
		}

	}
}

//	Write wcar_t*
void CTextFileWrite::Write(const wchar_t* utext)
{
	//	ASCII text file format?
	if ( ASCII == m_encoding )
	{
		//	Convert to string and write
		std::string text;
		WcharToString(utext, text);
		WriteAsciiString(text.c_str());
	}
	else
	{
		while ( 0 != *utext )
		{
			WriteWchar(*utext);
			++utext;
		}
	}
}

//Close the file
void CTextFileWrite::Close()
{
	if ( IsOpen() )
	{
		Flush();
	}

	CTextFileBase::Close();
}

CTextFileRead::CTextFileRead(const FILENAMECHAR* filename)
{
	m_hFile = ::CreateFile(filename,
	                       GENERIC_READ,
	                       FILE_SHARE_READ,
	                       NULL,
	                       OPEN_EXISTING,
	                       FILE_ATTRIBUTE_NORMAL,
	                       NULL);

	m_bFirstLine = true;
	m_bEndOfFile = ( IsOpen() == 0 );

	//	Force reading to buffer next time
	m_nBuffPos = -1;

	m_bUseExtraBuffer = false;

	ReadBOM();
}

void CTextFileRead::ReadBOM()
{
	if ( IsOpen() )
	{
		unsigned char bytes[2];

		//	Read the first two bytes
		ReadByte(bytes[0]);
		ReadByte(bytes[1]);

		//	Figure out what format the file is in
		if ( bytes[0] == 0xFF && bytes[1] == 0xFE )
		{
			m_encoding = UNI16_LE;
		}
		else if ( bytes[0] == 0xFE && bytes[1] == 0xFF )
		{
			m_encoding = UNI16_BE;
		}
		else if ( bytes[0] == 0xEF && bytes[1] == 0xBB )
		{
			//	This is probably UTF-8, check the third byte
			unsigned char temp;
			ReadByte(temp);
			if ( temp == 0xBF )
			{
				m_encoding = UTF_8;
			}
			else
			{
				//	Set text format.
				m_encoding = ASCII;
				ResetFilePointer();
			}
		}
		else
		{
			m_encoding = ASCII;

			//	Set start position.
			ResetFilePointer();
		}
	}
}

//	End of file?
bool CTextFileRead::Eof() const
{
	return m_bEndOfFile;
}


//	Read one byte
void CTextFileRead::ReadByte(unsigned char& ch)
{
	//	Use extra buffer if needed
	if ( m_bUseExtraBuffer )
	{
		m_bUseExtraBuffer = false;
		ch = m_extraBuffer_char;

		return;
	}

	//	In Windows, do this...

	//	If buffer used or not read
	if ( -1 == m_nBuffPos || BUFFSIZE - 1 == m_nBuffPos )
	{
		DWORD dwRead = ULONG_MAX;

		if ( !::ReadFile(m_hFile, m_buf, BUFFSIZE, &dwRead, NULL) )
		{
			//	Couldn't read!
			Close();
			m_nBuffSize = 0;

			//	Throw exception
			throw CTextFileException(GetLastError());
		}
		else
		{
			m_nBuffSize = (int) dwRead;
		}

		if ( 0 == m_nBuffSize )
		{
			m_bEndOfFile = true;
			ch = 0;

			return;
		}

		m_nBuffPos = 0;
	}
	else
	{
		++m_nBuffPos;

		if ( m_nBuffPos >= m_nBuffSize )
		{
			m_bEndOfFile = true;
			ch = 0;

			return;
		}
	}

	ch = m_buf[m_nBuffPos];
}


void CTextFileRead::ReadWchar(wchar_t& ch)
{
	if ( m_bUseExtraBuffer )
	{
		m_bUseExtraBuffer = false;
		ch = m_extraBuffer_wchar;

		return;
	}

	if ( UTF_8 == m_encoding )
	{
		//	This is quite tricky :-/
		//	http://www.cl.cam.ac.uk/~mgk25/unicode.html#examples
		unsigned char byte;
		ReadByte(byte);

		int nOnesBeforeZero = 0;

		{
			//	Calculate how many ones before the first zero...
			unsigned char temp = byte;

			while ( (temp & 0x80) != 0 )
			{
				temp = (unsigned char)(temp << 1);
				++nOnesBeforeZero;
			}
		}

		if ( 0 == nOnesBeforeZero )
		{
			ch = byte;

			return;
		}
		else if ( 2 == nOnesBeforeZero )
		{
			//	U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
			unsigned char byteb;
			ReadByte(byteb);

			ch = (wchar_t)( ((0x1F & byte) << 6) | (0x3F & byteb) );

			return;
		}
		else if ( 3 == nOnesBeforeZero )
		{
			//	U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
			unsigned char byteb, bytec;
			ReadByte(byteb);
			ReadByte(bytec);

			ch = (wchar_t)( ((0x0F & byte) << 12) | ((0x3F & byteb) << 6) | (0x3F & bytec) );

			return;
		}

		//	This should never happened! It it do, something is wrong with the file.
		ch = 0xFFFD;
	}
	else
	{
		unsigned char bytes[2];
		ReadByte(bytes[0]);
		ReadByte(bytes[1]);

		if ( UNI16_BE == m_encoding )
		{
			ch = (wchar_t) ( ((wchar_t) bytes[0] << 8) | (wchar_t) bytes[1] );
		}
		else
		{
			ch = (wchar_t) ( ((wchar_t) bytes[1] << 8) | (wchar_t) bytes[0] );
		}
	}
}


void CTextFileRead::ResetFilePointer()
{
	m_bUseExtraBuffer = false;

	::SetFilePointer(m_hFile, 0, NULL, FILE_BEGIN);

	//	Force reread buffer
	m_nBuffPos = -1;

	m_bFirstLine = true;
	m_bEndOfFile = false;
}


bool CTextFileRead::ReadLine(std::string& line)
{
	//	EOF?
	if ( Eof() )
	{
		return false;
	}

	if ( ASCII == m_encoding )
	{
		return ReadCharLine(line);
	}

	std::wstring wline;

	if ( !ReadWcharLine(wline) )
	{
		return false;
	}

	//	Convert
	WcharToString(wline.c_str(), line);

	return true;
}

bool CTextFileRead::ReadLine(std::wstring& line)
{
	//	EOF?
	if ( Eof() )
	{
		return false;
	}

	if ( ASCII == m_encoding )
	{
		std::string cline;

		if ( !ReadCharLine(cline) )
		{
			return false;
		}

		//	Convert to wstring
		CharToWstring(cline.c_str(), line);

		return true;
	}

	return ReadWcharLine(line);
}


bool CTextFileRead::Read(std::string& all, const std::string newline)
{
	if ( !IsOpen() )
	{
		return false;
	}

	int nBuffSize = GuessCharacterCount() + 2;
	int nBuffPos = 0;

	//	Create buffer
	char* pBuffer = new char[nBuffSize];

	//	If not possible, don't use any buffer
	if ( NULL == pBuffer )
	{
		nBuffSize = 0;
	}

	std::string temp;
	all = temp;
	all.reserve(nBuffSize);
	bool bFirstLine = true;

	while ( !Eof() )
	{
		if ( ReadLine(temp) )
		{
			//	Add new line, if not first line
			if ( !bFirstLine )
			{
				temp.insert(0, newline.c_str());
			}
			else
			{
				bFirstLine = false;
			}

			//	Add to buffer if possible
			if ( nBuffPos + (int) temp.size() < nBuffSize )
			{
				strcpy_s(pBuffer + nBuffPos, nBuffSize - nBuffPos, temp.c_str());
				nBuffPos += (int) temp.size();
			}
			else
			{
				//	Copy to all string
				if ( 0 != nBuffPos )
				{
					all.append(pBuffer, nBuffPos);
					nBuffPos = 0;
				}

				all += temp;
			}
		}
	};

	//	Copy to all string
	if ( 0 != nBuffPos )
	{
		all.append(pBuffer, nBuffPos);
	}

	if ( NULL != pBuffer )
	{
		delete [] pBuffer;
	}

	return true;
}

bool CTextFileRead::Read(std::wstring& all, const std::wstring newline)
{
	if ( !IsOpen() )
	{
		return false;
	}

	int nBuffSize = GuessCharacterCount() + 2;
	int nBuffPos = 0;

	//	Create buffer
	wchar_t* pBuffer = new wchar_t[nBuffSize];

	//	If not possible, don't use any buffer
	if ( NULL == pBuffer )
	{
		nBuffSize = 0;
	}

	std::wstring temp;
	all = temp;
	all.reserve(nBuffSize);
	bool bFirstLine = true;

	while ( !Eof() )
	{
		if ( ReadLine(temp) )
		{
			//	Add new line, if not first line
			if ( !bFirstLine )
			{
				temp.insert(0, newline.c_str());
			}
			else
			{
				bFirstLine = false;
			}

			//	Add to buffer if possible
			if ( nBuffPos + (int) temp.size() < nBuffSize )
			{
				wcscpy_s(pBuffer + nBuffPos, nBuffSize - nBuffPos, temp.c_str());
				nBuffPos += (int) temp.size();
			}
			else
			{
				//	Copy to all string
				if ( 0 != nBuffPos )
				{
					all.append(pBuffer, nBuffPos);
					nBuffPos = 0;
				}

				all += temp;
			}
		}
	};

	//	Copy to all string
	if ( 0 != nBuffPos )
	{
		all.append(pBuffer, nBuffPos);
	}

	if ( NULL != pBuffer )
	{
		delete [] pBuffer;
	}

	return true;
}

int CTextFileRead::GuessCharacterCount()
{
	//	Code needed to get file size when not using MFC
	int nByteCount = 1024 * 1024; //Default: 1 MB

	//	If ASCII, the number of characters is the byte count.
	//	If UTF-8, it can't be more than nByteCount, so use byte count
	if ( ASCII == m_encoding || UTF_8 == m_encoding )
	{
		return nByteCount;
	}

	//	Otherwise, every two byte in one character
	return nByteCount / 2;
}

//	Returns false if end-of-file was reached
//	(line will not be changed). If returns true,
//	it means that last line ended with a line break.
bool CTextFileRead::ReadWcharLine(std::wstring& line)
{
	//EOF?
	if ( Eof() )
	{
		return false;
	}

	wchar_t ch = 0;

	//	Ignore 0x0D and 0x0A
	//	or just 0x0D
	//	or just 0x0A
	//	except when we read the first line
	ReadWchar(ch);

	if ( !m_bFirstLine )
	{
		if ( ch == 0x0D ) //If next is 0x0A, ignore that too
		{
			ReadWchar(ch);

			if ( ch == 0x0A )
			{
				ReadWchar(ch);
			}
		}
		else if ( ch == 0x0A )
		{
			ReadWchar(ch);
		}
	}
	else
	{
		//	Next time we reads we don't read the first line in file.
		//	(then we should ignore \r\n)
		m_bFirstLine = false;
	}

	//	Clear line
	line.clear();

	//	It would be a lot easier if we didn't use a buffer, and added directly to
	//	line, but that is quite slow.
	wchar_t buffer[BUFFSIZE];
	buffer[BUFFSIZE-1] = '\0';
	//	Where to insert next character
	int nBufPos = 0;

	//	Read line
	while ( ch != 0x0D && ch != 0x0A && !Eof() )
	{
		//	End of buffer?
		if ( nBufPos + 1 >= BUFFSIZE )
		{
			//	Add to line
			line.append(buffer, nBufPos);
			nBufPos = 0;
		}

		buffer[nBufPos] = ch;
		++nBufPos;

		ReadWchar(ch);
	};

	buffer[nBufPos] = L'\0';
	line += buffer;

	//	Save currents character in extra buffer
	m_bUseExtraBuffer = true;
	m_extraBuffer_wchar = ch;

	return true;
}

//	Returns false if end-of-file was reached
//	(line will not be changed). If returns true,
//	it means that last line ended with a line break.
bool CTextFileRead::ReadCharLine(std::string& line)
{
	//	EOF?
	if ( Eof() )
	{
		return false;
	}

	unsigned char ch = 0;

	//	Ignore 0x0D and 0x0A
	//	or just 0x0D
	//	or just 0x0A
	//	except when we read the first line
	ReadByte(ch);

	if ( !m_bFirstLine )
	{
		if ( ch == 0x0D ) //If next is 0x0A, ignore that too
		{
			ReadByte(ch);

			if ( ch == 0x0A )
			{
				ReadByte(ch);
			}
		}
		else if ( ch == 0x0A )
		{
			ReadByte(ch);
		}
	}
	else
	{
		//	Next time we reads we don't read the first line in file.
		//	(then we should ignore \r\n)
		m_bFirstLine = false;
	}

	//	Clear line
	line.clear();

	//	It would be a lot easier if we didn't use a buffer, and added directly to
	//	line, but that is quite slow.
	char buffer[BUFFSIZE];
	buffer[BUFFSIZE-1] = '\0';
	//	Where to insert next character
	int nBufpPos = 0;

	//	Read line
	while ( ch != 0x0D && ch != 0x0A && !Eof() )
	{
		//	End of buffer?
		if ( nBufpPos + 1 >= BUFFSIZE )
		{
			//	Add to line
			line.append(buffer, nBufpPos);
			nBufpPos = 0;
		}

		buffer[nBufpPos] = ch;
		++nBufpPos;

		ReadByte(ch);
	};

	buffer[nBufpPos] = L'\0';
	line += buffer;

	//	Save currents character in extra buffer
	m_bUseExtraBuffer = true;
	m_extraBuffer_char = ch;

	return true;
}
